package cn.tedu.orderservice.service.impl;

import cn.tedu.charging.common.pojo.dto.GunStatusParam;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.common.pojo.vo.StationInfoVO;
import cn.tedu.charging.common.pojo.vo.UserVO;
import cn.tedu.charging.common.response.JsonResult;
import cn.tedu.charging.common.response.StatusCode;
import cn.tedu.orderservice.constant.Constant;
import cn.tedu.orderservice.feign.CostClient;
import cn.tedu.orderservice.feign.DeviceClient;
import cn.tedu.orderservice.feign.UserClient;
import cn.tedu.orderservice.job.DeviceCheckJob;
import cn.tedu.orderservice.mapper.OrderMapper;
import cn.tedu.orderservice.pojo.dto.CarInfoDto;
import cn.tedu.orderservice.pojo.dto.ChargingDto;
import cn.tedu.orderservice.pojo.dto.OrderParam;
import cn.tedu.orderservice.pojo.dto.StationInfoDto;
import cn.tedu.orderservice.pojo.po.ChargingBillSuccessPO;
import cn.tedu.orderservice.pojo.vo.OrderDetailVO;
import cn.tedu.orderservice.rabbitmq.RabbitMQProducer;
import cn.tedu.orderservice.repository.OrderRepository;
import cn.tedu.orderservice.service.OrderService;
import cn.tedu.orderservice.util.SendMegByMqtt;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.interceptor.CacheOperationSource;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.Random;
import java.util.UUID;

@Slf4j
@Service
@Transactional
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private UserClient userClient;
    @Autowired
    private DeviceClient deviceClient;
    @Autowired
    private SendMegByMqtt sendMegByMqtt;
    @Autowired
    private RabbitMQProducer rabbitMQProducer;
    @Autowired
    private CostClient costClient;

    @Override
    public String create(OrderParam orderParam) {
        log.debug("S1-创建订单参数:{}",orderParam);
        //获取订单id
        String orderNum = getOrderNo();
        log.debug("S2-获取订单id:{}",orderNum);
        try {
            DeviceCheckJob deviceCheckJob=new DeviceCheckJob(orderNum,orderParam.getGunId());
            log.debug("S2-1-设备自检:{}",deviceCheckJob);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //获取场站信息
        StationInfoDto stationInfoDto = getStationInfo(orderParam.getGunId());
        log.debug("S3-场站信息:{}",stationInfoDto);
        //获取车辆信息
        CarInfoDto carInfoDto = getCarInfo(orderParam.getUserId());
        log.debug("S4-车辆信息:{}",carInfoDto);
        //修改充电枪信息
        Boolean success = updateGunStatus(orderParam.getGunId(), Constant.GUN_STATS_BUSY);
        log.debug("S5-更新充电桩状态:{}",success);
        if (!success) {
            log.warn("修改枪状态失败,枪id:{},状态:{}",orderParam.getGunId(),Constant.GUN_STATS_BUSY);
        }
        //开始充电
        startCharging(orderNum,orderParam.getPileId(),orderParam.getGunId());
        //给rabbitmq发送消息
        rabbitMQProducer.sendOrder(orderNum);

        return orderNum;
    }

    /**
     * 用户手动停止充电
     * @param orderNo
     * @return
     */
    @Override
    public OrderDetailVO normalStopCharging(String orderNo) {
        ChargingBillSuccessPO chargingBillSuccessPO=getOrderInfoByOrderNo(orderNo);
        log.debug("查询订单详细信息:{}",chargingBillSuccessPO);
        //获取总花费
        ChargingProcessVO chargingProcessVO = getChargingProcessVO(orderNo);
        BigDecimal totalCost = chargingProcessVO.getTotalCost();
        log.debug("获取总消费:{}",totalCost);
        //更新订单状态
        orderRepository.updateStatus2NormalStop(orderNo);
        log.debug("更新订单状态");
        //更新枪状态
        updateGunStatus(chargingBillSuccessPO.getGunId(),Constant.GUN_STATS_FREE);
        log.debug("更新充电桩信息");
        //获取用户余额
        BigDecimal balance=new BigDecimal(0);
        log.debug("根据用户id:{}查询信息",chargingBillSuccessPO.getUserId());
        JsonResult<BigDecimal> userResult = userClient.getUserBalance(chargingBillSuccessPO.getUserId());
        if (userResult!=null){
            balance=userResult.getData();
            log.debug("获取余额信息");
        }
        //扣费后的余额
        BigDecimal currentBanance = balance.subtract(totalCost);
        //更新余额
        updateCurrentBalance(currentBanance,chargingBillSuccessPO.getUserId());

        OrderDetailVO orderDetailVO=new OrderDetailVO();
        orderDetailVO.setOrderNo(orderNo);
        orderDetailVO.setBalance(currentBanance);
        orderDetailVO.setTotalCost(Float.parseFloat(totalCost.toString()));
        orderDetailVO.setHours(chargingProcessVO.getHours());
        orderDetailVO.setMinutes(chargingProcessVO.getMinutes());
        orderDetailVO.setSeconds(chargingProcessVO.getSeconds());
        return orderDetailVO;
    }

    /**
     * 更新余额
     * @param currentBanance
     * @param userId
     */
    private void updateCurrentBalance(BigDecimal currentBanance, Long userId) {
        //更新余额
        log.debug("更新用户id:{},余额为:{}",userId,currentBanance);
    }

    /**
     * 获取花费信息
     * @param orderNo
     * @return
     */
    private ChargingProcessVO getChargingProcessVO(String orderNo) {
        JsonResult<ChargingProcessVO> result = costClient.getCostByOrderNo(orderNo);
        if (result!=null){
            ChargingProcessVO chargingProcessVO = result.getData();
            log.debug("获取消费数据:{}",chargingProcessVO);
            return chargingProcessVO;
        }
        return null;
    }

    /**
     * 根据orderNo查询订单信息
     * @param orderNo
     * @return
     */
    private ChargingBillSuccessPO getOrderInfoByOrderNo(String orderNo) {
        return orderRepository.getSuccess(orderNo);
    }

    /**
     * 异步下发充电指令给充电桩
     * @param orderNum
     * @param pileId
     * @param gunId
     */
    private void startCharging(String orderNum,Long pileId,Long gunId) {
        ChargingDto chargingDto = new ChargingDto();
        chargingDto.setGunId(gunId);
        chargingDto.setPileId(pileId);
        chargingDto.setMsg(Constant.START_CHARGING);
        chargingDto.setOrderNo(orderNum);
        log.debug("发送充电指令-入参:{}",chargingDto);
        sendMegByMqtt.sendMsg(chargingDto);
    }

    /**
     * 更新充电桩状态
     * @param gunId
     * @param status
     * @return
     */
    private Boolean updateGunStatus(Long gunId, Integer status) {
        GunStatusParam gunStatusParam= new GunStatusParam(gunId,status);
        log.debug("充电桩信息:{}",gunStatusParam);
        JsonResult jsonResult = deviceClient.updateGunStatus(gunStatusParam);
        if (jsonResult!=null){
            if (StatusCode.OPERATION_SUCCESS.getCode().equals(jsonResult.getCode())){
                return true;
            }
        }
        return false;
    }


    /**
     * 通过用户id获取用户绑定的车辆信息
     * 先假定一个用户只能绑定一辆车 充电的时候就是用户绑定的那辆唯一的车
     * 如果后续功能迭代 充电的时候由用户选择给哪辆车充电, 车的id可以通过入参的方式传入
     * @param userId
     * @return
     */
    private CarInfoDto getCarInfo(Long userId) {
        return getCarInfoByFeign(userId);
        //return getCarInfoByRestTemplate(userId);
    }

    /**
     * 通过Feign方式调用接口
     * @param userId
     * @return
     */
    private CarInfoDto getCarInfoByFeign(Long userId){
        CarInfoDto carInfoDto = new CarInfoDto();
        JsonResult<UserVO> userInfo = userClient.getUserInfo(userId);
        if (userInfo != null) {
            UserVO data = userInfo.getData();
            if (data != null) {
                BeanUtils.copyProperties(data, carInfoDto);
            }
        }
        return carInfoDto;
    }

    /**
     * 通过随机算法实现负载均衡
     * @return
     */
    public String chooseUrl(){
        //已知有3个用户服务
        String url = "http://localhost:8083//v1/user/info/{1}"; //占位符号
        String url1 = "http://localhost:8082/v1/user/info/{1}"; //占位符号
        //String url2 = "http://localhost:8085/user/info/{1}"; //占位符号

        //负载均衡算法 简单实现随机
        //ArrayList 和数组有什么区别? 自己去实现基于ArrayList

        String[] servers = new String[2];
        servers[0] = url;
        servers[1] = url1;
        //servers[2] = url2;
        Random random = new Random();
        int r = random.nextInt(servers.length);
        String server = servers[r];
        return server;
    }
    /**
     * 通过RestTemplate方式调用接口
     * @param userId
     * @return
     */
    private CarInfoDto getCarInfoByRestTemplate(Long userId){
        String url = chooseUrl();
        log.debug("通过restTemplate调用用户接口获取用户信息-获取url:{}",url);
        log.debug("通过restTemplate调用用户接口获取用户信息-入参:{}",userId);
        //这个方法,不能指定转换数据的类型
        //ResponseEntity<JsonResult> entity = restTemplate.getForEntity(url, JsonResult.class, 1);

        //指定返回的类型
        ParameterizedTypeReference<JsonResult<UserVO>>response=new ParameterizedTypeReference<JsonResult<UserVO>>() {};
        //调用别的接口
        ResponseEntity<JsonResult<UserVO>> entity = restTemplate.exchange(url, HttpMethod.GET, null, response, 1);
        log.debug("通过restTemplate调用用户接口获取用户信息-入参:{},出参:{}",userId,entity);
        if(entity != null) {
            HttpStatus statusCode = entity.getStatusCode();
            if (HttpStatus.OK == statusCode) {
                //强制转换也不可以,Object和原来的对象没有继承关系
                //把data的数据转成字符串,但不是JSON格式,所以无法转成原来的对象
/*                JsonResult body = entity.getBody();
                Object data = body.getData();
                ObjectMapper objectMapper=new ObjectMapper();
                CarInfoDto carInfoDto = null;
                try {
                    carInfoDto = objectMapper.readValue(String.valueOf(data), CarInfoDto.class);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }*/
                UserVO userVO = entity.getBody().getData();
                CarInfoDto carInfoDto=new CarInfoDto();
                carInfoDto.setCarId(userVO.getCarId());
                return carInfoDto;
            }
        }
        return null;
    }

    /**
     * 通过枪id获取运营商信息和充电站信息
     * @param gunId
     * @return StationInfoDto
     */
    private StationInfoDto getStationInfo(Long gunId) {
        StationInfoDto stationInfoDto=new StationInfoDto();
        JsonResult<StationInfoVO> body = deviceClient.getStationInfo(gunId);
        if (body!=null){
            StationInfoVO stationInfoVO = body.getData();
            if (stationInfoVO!=null){
                BeanUtils.copyProperties(stationInfoVO,stationInfoDto);
            }
        }
        return stationInfoDto;
    }

    /**
     * 获取订单id
     * @return
     */
    private String getOrderNo() {
        String uuid = UUID.randomUUID().toString();
        long currentTimeMillis = System.currentTimeMillis();
        return uuid+currentTimeMillis;
    }
}
